/*NOTICE

This software was produced for the U. S. Government
under Contract No. FA8721-09-C-0002, and is
subject to the Rights in Noncommercial Computer Software
and Noncommercial Computer Software Documentation Clause
(DFARS) 252.227-7014 (JUN 1995)

(c) 2009 The MITRE Corporation. All Rights Reserved.
*/
package org.mitre.xmltransform.spmif;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.w3c.dom.Element;
import org.mitre.xmltransform.spmif.HierarchyStep;

public class StepState {
	private static final Device LOCATION_NONE = new Device("<<NONE>>", Device.TYPE_PROCESSING, null, 1);

	public StepState(Element pPerformanceScenarioElement, Element pExecutionGraphElement, String pName, StepState pParentState, Device pLocation) {
		mPerformanceScenarioElement = pPerformanceScenarioElement;
		mExecutionGraphElement = pExecutionGraphElement;
		mParentState = pParentState;
		mOpenSteps = new Stack<HierarchyStep>();
		mCurStep = null;
		mRootSteps = new ArrayList<HierarchyStep>();
		mStepMap = new HashMap<String, HierarchyStep>();
		mLocation = pLocation;
		mName = pName;
	}
	
	public Element getPerformanceScenarioElement() {
		return mPerformanceScenarioElement;
	}
	
	public Element getExecutionGraphElement() {
		return mExecutionGraphElement;
	}
	
	public String getName() {
		return mName;
	}
/*	public void setLocation(String pLocation) {
		if (getNumberOfRootSteps() > 0) {
			System.err.println("WARNING: Changing location with existing steps!");
		}
		mLocation = pLocation;
	}*/
	public Device getLocation() {
		return mLocation;
	}
	
	public boolean isSameLocation(Device pLocation) {
		if (mLocation == null) { // not set yet
			mLocation = pLocation;
			return true;
		} else if (pLocation == null) {
			return LOCATION_NONE.equals(mLocation);
		} else {
			return mLocation.equals(pLocation);
		}
	}

	public void addParentStep(HierarchyStep pNewStep) {
		if (mCurStep != null) {
			//System.out.println("    add as a child to curStep and push curStep on stack");
			mCurStep.addChild(pNewStep);
			mOpenSteps.push(mCurStep);
			//System.out.println("    stack now has " + mOpenSteps.size() + " elements.");
		} else {
			//System.out.println("    add as a root step");
			mRootSteps.add(pNewStep);
		}
		mCurStep = pNewStep;
	}
	
	public boolean closeParentStep(String pExecId) {
		if (mCurStep == null) {
			if (mParentState == null) {
				System.out.println("--> null curStep when null parent state.");
				return false;
			}
			return mParentState.closeParentStep(pExecId);
		} else {
			if (pExecId != null && !mCurStep.getExecId().equals(pExecId)) {
				throw new IllegalArgumentException("Received finish for " + pExecId + " but expected finish exec for " + mCurStep.getExecId());
			}
			if (mOpenSteps.isEmpty()) {
				//System.out.println("    stack is empty. make curStep null");
				mCurStep = null;
			} else {
				//System.out.println("    pop new curStep off stack. Before stack size = " + mOpenSteps.size());
				mCurStep = mOpenSteps.pop();
			}
			return true;
		}			
	}
	
	public void addChildStep(HierarchyStep pNewStep) {
		if (mCurStep == null) {
			//System.out.println("    add as a root step. no current step.");
			mRootSteps.add(pNewStep);
		} else {
			//System.out.println("    add as child to curStep. no change to current step.");
			mCurStep.addChild(pNewStep);
		}			
	}
	
	public void addToStepMap(String pId, HierarchyStep pNewStep) {
		if (pNewStep != null) {
			mStepMap.put(pId, pNewStep);
		}			
	}
	
	public boolean isValidEndState() {
		return mOpenSteps.isEmpty();
	}
	
	public int getNumberOfRootSteps() {
		return mRootSteps.size();
	}
	
	public HierarchyStep getRootStep(int pIndex) {
		return mRootSteps.get(pIndex);
	}
	
	private String mName;
	private Device mLocation;
	private StepState mParentState;
	private Stack<HierarchyStep> mOpenSteps;
	private HierarchyStep mCurStep;
	private List<HierarchyStep> mRootSteps;
	private Map<String, HierarchyStep> mStepMap;
	private Element mPerformanceScenarioElement;
	private Element mExecutionGraphElement;
}